﻿namespace BadHabit.WPFTools
{
    using System;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;

    /// <summary>
    /// Microsoft Datagrid has some limitations, such as:
    /// 
    /// - Navigation keys when at the EditMode
    /// - Set focus on edit control at templatecollumns
    /// - Is the datagrid at EditMode?
    /// 
    /// To solve those issues we create this class.
    /// </summary>
    public class DataGrid : Microsoft.Windows.Controls.DataGrid
    {
        internal static readonly DependencyPropertyKey IsEditingPropertyKey = DependencyProperty.RegisterReadOnly(
            "IsEditing",
            typeof(bool),
            typeof(DataGrid),
            null);

        /// <summary>
        /// IEditing Dependency Property.
        /// </summary>
        public static readonly DependencyProperty IsEditingProperty = IsEditingPropertyKey.DependencyProperty;

        /// <summary>
        /// Returns true if there is a cell being editing. This is a Dependency Property.
        /// </summary>
        public bool IsEditing
        {
            get
            {
                return (bool)GetValue(IsEditingProperty);
            }

            private set
            {
                SetValue(IsEditingPropertyKey, value);
            }
        }

        /// <summary>
        /// Sets and Gets whenever should use navigations extensions or not.
        /// </summary>
        public bool UseNavigationExtensions { get; set; }

        /// <summary>
        /// Whenever a cell begins to be edited set the IsEditing Property.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnBeginningEdit(Microsoft.Windows.Controls.DataGridBeginningEditEventArgs e)
        {
            base.OnBeginningEdit(e);

            this.IsEditing = true;
        }

        /// <summary>
        /// Whenever a cell ends to be edited set the IsEditing Property.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnCellEditEnding(Microsoft.Windows.Controls.DataGridCellEditEndingEventArgs e)
        {
            base.OnCellEditEnding(e);
            this.IsEditing = false;
        }

        /// <summary>
        /// Whenever a cell starts the edit mode, set focus on the first control of the cell that have TABSTOP.
        /// This is a workaround for templatecolumns.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPreparingCellForEdit(Microsoft.Windows.Controls.DataGridPreparingCellForEditEventArgs e)
        {
            base.OnPreparingCellForEdit(e);
            
            if (!this.UseNavigationExtensions)
            {
                return;
            }

            Control control = GetNextTabStopControlObjectInVisualTree(e.EditingElement);
            if (control != null && control.IsEnabled && control.IsTabStop)
            {
                control.Focus();
            }
        }

        /// <summary>
        /// Now you can use UP/DOWN arrows even at the edit mode.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPreviewKeyDown(KeyEventArgs e)
        {
            base.OnPreviewKeyDown(e);
            if (!this.UseNavigationExtensions)
            {
                return;
            }

            // If is editing and UP/DOWN was presees move to the up/down rows
            if (this.IsEditing)
            {
                UIElement focused = Keyboard.FocusedElement as UIElement;
                if (focused == null)
                {
                    return;
                }

                if (focused is System.Windows.Controls.ComboBox)
                {
                    return;
                }

                if (e.Key == Key.Down)
                {
                    CommitEdit();
                    focused.MoveFocus(new TraversalRequest(FocusNavigationDirection.Down));
                }

                if (e.Key == Key.Up)
                {
                    CommitEdit();
                    focused.MoveFocus(new TraversalRequest(FocusNavigationDirection.Up));
                }
            }
            else
            {
                // This is a work around for get focus on Template columns.
                if (e.Key != Key.Left && e.Key != Key.Right && e.Key != Key.Down && e.Key != Key.Up)
                {
                    if (!(CurrentColumn is Microsoft.Windows.Controls.DataGridTemplateColumn))
                    {
                        return;
                    }

                    BeginEdit();
                }
            }
        }

        public static Control GetNextTabStopControlObjectInVisualTree(System.Windows.DependencyObject dpob)
        {
            int count = System.Windows.Media.VisualTreeHelper.GetChildrenCount(dpob);
            for (int i = 0; i < count; i++)
            {
                System.Windows.DependencyObject child = System.Windows.Media.VisualTreeHelper.GetChild(dpob, i);
                Control childControl = child as Control;
                if (childControl != null && childControl.Focusable && childControl.IsTabStop)
                {
                    return childControl;
                }

                childControl = GetNextTabStopControlObjectInVisualTree(child);
                if (childControl != null)
                {
                    return childControl;
                }
            }

            return null;
        }
    }
}
